#include "js-engine.h"
#include "js-functions.h"
#include "desktopClasses.h"
#include "XMLHttpRequestClass.h"

#include <glib.h>

#define RUNTIME_MEMORY 1L*1024*1024
#define CHUNK_SIZE 8192
#define FAILED 1

JSBool
global_enumerate(JSContext * cx, JSObject * obj)
{
    return JS_TRUE;
}

JSBool
global_resolve(JSContext * cx, JSObject * obj, jsval id, uintN flags,
	       JSObject ** objp)
{
    return JS_TRUE;
}

JSClass         glob_class = 
    {
	"global", JSCLASS_NEW_RESOLVE,
	JS_PropertyStub, JS_PropertyStub,
	JS_PropertyStub, JS_PropertyStub,
	global_enumerate, (JSResolveOp) global_resolve,
	JS_ConvertStub, JS_FinalizeStub
    };

js_engine *js_engine_new()
{
    js_engine *new = (js_engine *)g_new0(js_engine,1);
    g_return_val_if_fail(new, NULL);
	
    new->rt = JS_NewRuntime(RUNTIME_MEMORY);
    g_return_val_if_fail(new->rt, NULL);
	
    new->cx = JS_NewContext(new->rt, CHUNK_SIZE);
    g_return_val_if_fail(new->cx, NULL);
	
    new->glob = JS_NewObject(new->cx, &glob_class, NULL, NULL);
    g_return_val_if_fail(new->glob, NULL);
    JS_InitStandardClasses(new->cx, new->glob);
	
    JS_DefineObject(new->cx, new->glob, "strings", NULL, NULL, 0);
    js_engine_define_functions(new);
    js_engine_define_constructors(new);
	
    return new;
}

gboolean js_engine_destroy(js_engine *engine)
{
    JS_DestroyContext(engine->cx);
    JS_DestroyRuntime(engine->rt);
	
    g_free(engine);
    g_debug("Destroyed js_engine");
    return TRUE;
}

gboolean js_engine_load_script_file(js_engine *engine, const gchar *filename)
{
    jsval rval = 0;
    JSBool result = 0;
    g_return_val_if_fail(filename, FALSE);
    GError *err = NULL;
    GIOChannel *fd = g_io_channel_new_file(filename, "r", &err);
    GIOStatus status;
    GString *script = g_string_new(NULL);
	
    if(err){
	g_error("Failed to open %s.", filename);
	return FALSE;
    }
    g_io_channel_set_encoding(fd, NULL, &err);
    if(err){
	g_error("Failed to set encoding %s.", filename);
	return FALSE;
    }

    while(TRUE){
	gchar ch; 
	gsize read_bytes;
	status = g_io_channel_read_chars(fd, &ch, 1, &read_bytes, &err);
	if(status != G_IO_STATUS_NORMAL)
	    break;
	if(g_ascii_isgraph(ch)){
	    g_string_append_c(script, ch);
	    break;
	}
    }
	
    while(TRUE){
	gchar ch; 
	gsize read_bytes;
	status = g_io_channel_read_chars(fd, &ch, 1, &read_bytes, &err);
	if(status != G_IO_STATUS_NORMAL)
	    break;
	g_string_append_c(script,ch);
    }
		
    result = JS_EvaluateScript(engine->cx, engine->view, script->str, script->len, NULL, 0,&rval);
    g_string_free(script,TRUE);
    g_return_val_if_fail(result, FALSE);
	
    return TRUE;
}

JSObject *js_engine_get_object(js_engine *engine, const gchar *name)
{
    JSBool result = JS_FALSE;
    jsval rval;
    GString *script_str = g_string_new("this.");
    g_string_append(script_str, name);
    result = JS_EvaluateScript(engine->cx, engine->view, script_str->str, script_str->len, NULL, 0, &rval);
    g_string_free(script_str, TRUE);
    if(result == JS_FALSE || JSVAL_IS_VOID(rval)){
	g_warning("fail to find %s object", name);
	return NULL;
    }else{
	g_message("return js_object %s", name);
	return JSVAL_TO_OBJECT(rval);
    }
}

gboolean js_engine_define_constructors(js_engine *engine)
{
    g_return_val_if_fail(engine, FALSE);
    XMLHttpRequestClass_define_constructor(engine);

    viewClass_init(engine);
    JSObject *basicElement_proto = basicElementClass_init(engine);
    elementsClass_init(engine, basicElement_proto);
    buttonClass_init(engine, basicElement_proto);
    checkboxClass_init(engine, basicElement_proto);
    objectClass_init(engine, basicElement_proto);
    contentareaClass_init(engine, basicElement_proto);
    divClass_init(engine, basicElement_proto);
    editClass_init(engine, basicElement_proto);
    imgClass_init(engine, basicElement_proto);
    labelClass_init(engine, basicElement_proto);
    aClass_init(engine, basicElement_proto);
    progressbarClass_init(engine, basicElement_proto);
    radioClass_init(engine, basicElement_proto);
    scrollbarClass_init(engine, basicElement_proto);
    listboxClass_init(engine, basicElement_proto);
    listitemClass_init(engine, basicElement_proto);

    optionsClass_init(engine);
    menuClass_init(engine);
	
    return TRUE;
}
